package xyz.mumuwei.youthcourseserver.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import xyz.mumuwei.youthcourseserver.common.dto.WechatMessageDto;
import xyz.mumuwei.youthcourseserver.common.lang.Const;
import xyz.mumuwei.youthcourseserver.common.lang.Result;
import xyz.mumuwei.youthcourseserver.mapper.UserMapper;
import xyz.mumuwei.youthcourseserver.pojo.User;
import xyz.mumuwei.youthcourseserver.service.WechatService;
import xyz.mumuwei.youthcourseserver.utils.JwtTokenUtil;
import xyz.mumuwei.youthcourseserver.utils.RedisUtil;

import java.util.HashMap;
import java.util.Map;

/**
 * @author mumuwei
 * @date 2022/4/5
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatServiceImpl implements WechatService {

    final UserMapper userMapper;
    final RedisUtil redisUtil;
    final RestTemplate restTemplate;

    @Value("${ycms.wechat.appid}")
    private String appid;

    @Value("${ycms.wechat.secret}")
    private String secret;

    @Value("${ycms.wechat.baseUrl}")
    private String baseUrl;

    /**
     * 微信登录
     *
     * @param code 用户登录凭证
     * @return 返回登录 token
     */
    @Override
    public Result login(String code){
        String openId = getOpenId(code);
        if ("".equals(openId)) {
            return Result.fail("登陆失败，请重试");
        }

        // 根据 openid 查询数据库，判断用户是否存在
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("open_id", openId));

        if (user == null) {
            return Result.fail("该微信未绑定");
        }
        // 脱敏
        user.setKeyword("");

        // 判断用户状态
        if (user.getState() == 1) {
            return Result.fail(Const.LOCK_CODE, Const.LOCK_MSG);
        }

        // 更新用户登录信息
        updateUserLoginInfo(user.getId());

        // 返回数据
        return Result.success("登录成功", generalToken(user));
    }

    /**
     * 绑定微信
     *
     * @param code 用户登录凭证
     * @param uid  用户id
     * @return 绑定状态
     */
    @Override
    public Result bandWechat(String code, Integer uid) {
        String openId = getOpenId(code);
        if ("".equals(openId)) {
            return Result.fail("登陆失败，请重试");
        }

        // 检查该微信是否被绑定
        User one = userMapper.selectOne(new QueryWrapper<User>().eq("open_id", openId));
        if (one != null) {
            return Result.fail("该微信已绑定其他账号，请先进行解绑");
        }

        // 未绑定，进行绑定
        User user = userMapper.selectById(uid);
        user.setOpenId(openId);

        int i = userMapper.updateById(user);
        if (i > 0) {
            return Result.success("绑定成功");
        }
        return Result.fail("绑定失败！");
    }

    /**
     * 登录凭证校验, 返回 openid， session_key， unionid 等
     * 详见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     * @param code 登录时获取的 code
     * @return 请求返回数据
     */
    private String authCode2Session(String code){
        HashMap<String, String> data = new HashMap<>(5);
        data.put("appid", appid);
        data.put("secret", secret);
        data.put("js_code", code);
        return restTemplate.getForEntity(
                baseUrl + "/sns/jscode2session?grant_type=authorization_code&appid={appid}&secret={secret}&js_code={js_code}",
                String.class,
                data
        ).getBody();
    }

    /**
     * 请求微信服务器， 获取 openid
     * @param code 登录时获取的 code
     * @return openid
     */
    private String getOpenId(String code) {
        // 请求微信服务器，获取用户 openid
        JSONObject jsonObject = JSON.parseObject(authCode2Session(code));
        String openid = jsonObject.get("openid").toString();
        // 请求失败处理
        if ("".equals(openid) || openid == null) {
            return "";
        }

        // 请求成功，并且返回数据
       return jsonObject.get("openid").toString();
    }

    /**
     * 更新用户登录信息
     * @param id 用户信息
     */
    private void updateUserLoginInfo(Integer id) {
        User user = new User();
        user.setId(id);
        // 更新用户登录时间
        user.setLastLoginTime(String.valueOf(System.currentTimeMillis()).substring(0,10));
        userMapper.updateById(user);
    }

    /**
     * 生成 token
     * @param user 用户信息
     * @return token信息
     */
    private Map<String, String> generalToken(User user) {
        // 创建token
        String token = JwtTokenUtil.generateToken(user);
        HashMap<String, String> data = new HashMap<>(2);
        data.put("token", token);
        return data;
    }

    /**
     * 获取小程序全局唯一后台接口调用凭据（access_token）
     * @return 返回access_token
     */
    private String authGetAccessToken() {
        // 检查缓存是否存在，存在直接使用
        String accessToken = (String) redisUtil.get(Const.TOKEN_WECHAT_HEAD_REDIS);
        if(accessToken != null && !"".equals(accessToken)) {
            return accessToken;
        }

        // 缓存中没有token
        HashMap<String, String> data = new HashMap<>(3);
        data.put("appid", appid);
        data.put("secret", secret);

        ResponseEntity<String> response = restTemplate.getForEntity(
                baseUrl + "/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}",
                String.class,
                data
        );

        // json转换 获取数据
        JSONObject jsonObject = JSON.parseObject(response.getBody());
        accessToken = jsonObject.get("access_token").toString();
        Integer expiresIn = (Integer) jsonObject.get("expires_in");

        // 存入redis
        redisUtil.set(Const.TOKEN_WECHAT_HEAD_REDIS, accessToken, expiresIn*1000);

        return accessToken;
    }

    /**
     * 微信发送订阅消息
     * @param wechatMessageDto 订阅消息参数 openid 模板id data
     * @return 发送结果 errcode: 0, errmsg: ok 发送成功
     */
    @Override
    public JSONObject subscribeMessageSend(WechatMessageDto wechatMessageDto) {
        if (wechatMessageDto.getMiniprogram_state() == null) {
            wechatMessageDto.setMiniprogram_state(Const.MINI_PROGRAM_STATE_WECHAT);
        }
        wechatMessageDto.setAccess_token(authGetAccessToken());
        wechatMessageDto.setPage("pages/index/index");

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<WechatMessageDto> httpEntity = new HttpEntity<>(wechatMessageDto, headers);
        ResponseEntity<String> response = restTemplate.postForEntity(
                baseUrl + "/cgi-bin/message/subscribe/send?access_token=" + wechatMessageDto.getAccess_token(),
                httpEntity,
                String.class
        );
        return JSONObject.parseObject(response.getBody());
    }

}
